#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <pthread.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include "Log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Daemon.hpp"

uint16_t defaultport = 8080;
const std::string defaultip = "0.0.0.0";
const int defaultfd = -1;
const int backlog = 10; // 这个数字一般不要设置的太大，这个参数在tcp协议的时候再来谈(允许挂起的最大连接请求数量)
extern Log lg;

enum
{
    UsageError = 1,
    SocketError,
    BindError,
    ListenError
};

class TcpServer;
class ThreadData
{
public:
    ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *t) : sockfd(fd), clientip(ip), clientport(port), tsvr(t)
    {
    }

public:
    int sockfd;
    std::string clientip;
    uint16_t clientport;
    TcpServer *tsvr; // 为了解决static成员函数不能访问类内成员与属性
};

class TcpServer
{
public:
    TcpServer(const uint16_t &port = defaultport, const std::string &ip = defaultip) : listen_sockfd_(defaultfd), port_(port), ip_(ip)
    {
    }
    void InitServer()
    {
        // 1.create socket
        listen_sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listen_sockfd_ < 0)
        {
            lg(Fatal, "create socket error,errno: %d,err_string: %s", errno, strerror(errno));
            exit(SocketError);
        }
        lg(Info, "create socket success,listen_sockfd_: %d", listen_sockfd_);

        int opt=1;
        setsockopt(listen_sockfd_,SOL_SOCKET,SO_REUSEADDR|SO_REUSEPORT,&opt,sizeof(opt)); //防止偶发性的服务器无法立即重启，端口复用问题（tcp协议详谈）

        struct sockaddr_in local; // 在arpa/inet.h中
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        local.sin_addr.s_addr = INADDR_ANY;

        inet_aton(ip_.c_str(), &(local.sin_addr));

        // 2.bind socket
        if (bind(listen_sockfd_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error,errno: %d,err_string: %s", errno, strerror(errno));
            exit(BindError);
        }
        lg(Info, "bind socket success,listen_sockfd_: %d", listen_sockfd_);

        // 3.listen
        // tcp是面向连接的，服务器一般是比较被动的，服务器一直处于一种一直在等待到来的状态
        if (listen(listen_sockfd_, backlog) < 0)
        {
            lg(Fatal, "listen error,errno: %d,err_string: %s", errno, strerror(errno));
            exit(ListenError);
        }
        lg(Info, "listen socket success,listen_sockfd_: %d", listen_sockfd_);
    }

    // static void *Routine(void *args)
    // {
    //     pthread_detach(pthread_self());
    //     ThreadData *td = static_cast<ThreadData *>(args);
    //     td->tsvr->Service(td->sockfd, td->clientip, td->clientport);
    //     delete td;
    //     return nullptr;
    // }

    void Start()
    {
        Daemon();
        //signal(SIGPIPE,SIG_IGN);
        // signal(SIGCHLD,SIG_IGN);
        ThreadPool<Task>::GetInstance()->Start();
        lg(Info, "tcpSever is running...");
        for (;;)
        {
            // 1.获取新连接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);

            // 此处的sockfd才是进行io的套接字
            int sockfd = accept(listen_sockfd_, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                // 获取连接失败还可以继续获取
                lg(Warning, "accept error,errno: %d,err_string: %s", errno, strerror(errno));
                continue;
            }

            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip)); // 转字符串

            // 2.根据新连接来进行通信
            lg(Info, "get a new link...,sockfd: %d, client ip: %s, client port: %d", sockfd, clientip, clientport);

            // version1---单进程版
            // Service(sockfd, clientip, clientport);
            // close(sockfd);

            // version2---多进程版
            // pid_t id = fork();
            // if (id == 0)
            // {
            //     // child
            //     close(listen_sockfd_); // 子进程不用这个，最好还是关了，避免子进程使用这个造成异常
            //     if (fork() > 0)
            //         exit(0);

            //     // 孙子进程,system领养
            //     Service(sockfd, clientip, clientport);
            //     close(sockfd);
            //     exit(0);
            // }
            // father
            //  close(sockfd);   //不关掉父进程能用的文件描述符会越来越少
            //  pid_t rid=waitpid(id,nullptr,0);
            //  (void)rid;

            // version3---多线程版
            // ThreadData *td = new ThreadData(sockfd, clientip, clientport, this); // 这里的this指针是一个指向TcpServer类型的指针
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);

            // version4---线程池
            Task t(sockfd,clientip,clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }

    // 传过来的clientip是一个char*，经过引用变成了一个string的对象，用%s打印就要调用c_str
    // void Service(int sockfd, const std::string &clientip, const uint16_t &clientport)
    // {
    //     // 测试代码
    //     char buffer[4096];
    //     while (true)
    //     {
    //         ssize_t n = read(sockfd, buffer, sizeof(buffer));
    //         if (n > 0)
    //         {
    //             buffer[n] = 0;
    //             std::cout << "client say# " << buffer << std::endl;
    //             std::string echo_string = "tcpserver echo# ";
    //             echo_string += buffer;

    //             write(sockfd, echo_string.c_str(), echo_string.size());
    //         }
    //         else if (n == 0) // 客户端退了，服务端什么都没读到，read返回0
    //         {
    //             lg(Info, "%s:%d quit,server close sockfd: %d", clientip.c_str(), clientport, sockfd);
    //             break;
    //         }
    //         else // 读时出现问题了就先break不管了
    //         {
    //             lg(Warning, "read error,sockfd: %d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);
    //             break;
    //         }
    //     }
    // }
    ~TcpServer()
    {
    }

private:
    int listen_sockfd_; // 这个套接字用来获取新连接，有了这个套接字才能获取新连接
    uint16_t port_;
    std::string ip_;
};
